package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.aliyun.gts.bpaas.middleware.cache.api.CacheManager;
import com.fasterxml.jackson.databind.JsonNode;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonGlobalParamSetHandle;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.MsgTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.NoticeTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowRuTimeoutSettingVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.User;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowNoticeTemplateVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowReNoticeInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowReNoticeInfoQueryVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowReTimeoutSettingVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowRuTaskNoticeChannelVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowNoticeTemplateRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceTraceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowReTimeoutSettingRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowReTaskNoticeChannel;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowReTimeoutSetting;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowReTimeoutSettingExample;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowReTimeoutSettingDAO;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.NoticeTemplateDAO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author lilh
 * @date 2019-07-11 16:29
 */
@Service
public class FlowReTimeoutSettingRepositoryImpl implements FlowReTimeoutSettingRepository {

    @Autowired
    private FlowReTimeoutSettingDAO flowReTimeoutSettingDAO;

    @Autowired
    private NoticeTemplateDAO noticeTemplateDAO;

    @Autowired
    private FlowRuTimeoutSettingRepositoryImpl flowRuTimeoutSettingService;

    @Autowired
    private FlowRuTaskNoticeChannelRepositoryImpl flowRuTaskNoticeChannelService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowNoticeTemplateRepository flowNoticeTemplateRepository;

    @Autowired
    private FlowProcessInstanceTraceRepository processInstanceTraceRepository;

    @Autowired(required = false)
    private CacheManager cacheManager;

    @Override
    public int insertFlowReTimeoutSetting(FlowReTimeoutSettingVo entity) {
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());
        return flowReTimeoutSettingDAO.insertSelective(convert(entity));
    }

    @Override
    public List<FlowReTimeoutSettingVo> listFlowReTimeoutSetting(String deploymentId, String nodeCode) {
        FlowReTimeoutSettingExample query = new FlowReTimeoutSettingExample();
        query.createCriteria().andDeployIdEqualTo(deploymentId).andActivityIdEqualTo(nodeCode);
        return listFlowReTimeoutSetting(query);
    }

    public List<FlowReTimeoutSettingVo> listFlowReTimeoutSetting(FlowReTimeoutSettingExample query) {
        return flowReTimeoutSettingDAO.selectByExample(query).stream().map(this::convert).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyReTimeoutSetting(String deployId, String procInstId, String activityId, String taskId) {
        try {
            FlowReNoticeInfoQueryVo flowReNoticeInfoQueryVo = new FlowReNoticeInfoQueryVo();
            flowReNoticeInfoQueryVo.setDeployId(deployId);
            flowReNoticeInfoQueryVo.setProcInstId(procInstId);
            flowReNoticeInfoQueryVo.setActivityId(activityId);
            flowReNoticeInfoQueryVo.setTaskId(taskId);

            //初始化复制的数据列表
            List<FlowReNoticeInfo> flowReNoticeInfos = null;
            if (activityId == null && taskId == null) {
                //流程级别高级配置超时和预警
                flowReNoticeInfos = noticeTemplateDAO.listNeedCopyReNoticeInfoForProcess(flowReNoticeInfoQueryVo);
            } else {
                // 任务级别超时和预警
                // 获取需要复制的数据,排除接口设置的数据
                flowReNoticeInfos = noticeTemplateDAO.listNeedCopyReNoticeInfo(flowReNoticeInfoQueryVo);
                // 给接口设置的数据设置taskId
                noticeTemplateDAO.updateRuNoticeInfo(flowReNoticeInfoQueryVo);
                noticeTemplateDAO.updateRuNoticeChannel(flowReNoticeInfoQueryVo);
            }

            // 按照通知类型进行分组
            Map<String, List<FlowReNoticeInfo>> flowReNoticeInfosGroup = flowReNoticeInfos.stream().collect(Collectors.groupingBy(FlowReNoticeInfo::getType));

            // 获取单个线程全局设置的用户信息
            User userInfo = CommonGlobalParamSetHandle.getUser();

            // 如果用户信息为空,则去查询
            User currentUser = null;
            if (Objects.isNull(userInfo)) {
                if (!CommonOperateLogHolder.getFromSdk()) {
                    currentUser = userRepository.getCurrentUser();
                }
            }

            // 进行复制，分别复制到运行时通知设置表和运行时通知渠道设置表
            for (Map.Entry<String, List<FlowReNoticeInfo>> entry : flowReNoticeInfosGroup.entrySet()) {
                List<FlowReNoticeInfo> value = entry.getValue();
                for (FlowReNoticeInfo flowReNoticeInfo : value) {
                    FlowRuTimeoutSettingVo flowRuTimeoutSetting = new FlowRuTimeoutSettingVo();
                    BeanUtils.copyProperties(flowReNoticeInfo, flowRuTimeoutSetting);
                    flowRuTimeoutSetting.setProcInstId(procInstId);
                    flowRuTimeoutSetting.setTaskId(taskId);
                    flowRuTimeoutSetting.setIsNotice("Y");
                    if (Objects.nonNull(currentUser)) {
                        flowRuTimeoutSetting.setCreateBy(currentUser.getUserName());
                    }
                    flowRuTimeoutSettingService.insertFlowRuTimeoutSetting(flowRuTimeoutSetting);

                    FlowRuTaskNoticeChannelVo flowRuTaskNoticeChannel = new FlowRuTaskNoticeChannelVo();
                    flowRuTaskNoticeChannel.setProcInstId(procInstId);
                    flowRuTaskNoticeChannel.setActivityId(activityId);
                    flowRuTaskNoticeChannel.setTaskId(taskId);
                    flowRuTaskNoticeChannel.setChannel(flowReNoticeInfo.getChannel());
                    String staCode = flowReNoticeInfo.getStaCode();
                    //todo 针对发起人特殊处理
                    if (Objects.equals(flowReNoticeInfo.getStaType(), ConstanceVariable.STARTER_TYPE)) {
                        //发起人
                        staCode = resolveStarter(procInstId);
                    }
                    flowRuTaskNoticeChannel.setStaCode(staCode);
                    flowRuTaskNoticeChannel.setStaType(flowReNoticeInfo.getStaType());
                    flowRuTaskNoticeChannel.setStaJson(flowReNoticeInfo.getStaJson());
                    flowRuTaskNoticeChannel.setRuTimeoutSettingId(flowRuTimeoutSetting.getId());
                    flowRuTaskNoticeChannel.setSource("S");
                    flowRuTaskNoticeChannelService.insertFlowRuTaskNoticeChannel(flowRuTaskNoticeChannel);
                    if (flowRuTimeoutSetting.getType().equals(MsgTypeEnum.PROCESS_TIMEOUT_NOTICE.getCode()) ||
                            flowRuTimeoutSetting.getType().equals(MsgTypeEnum.PROCESS_WARNING_NOTICE.getCode()) ||
                            flowRuTimeoutSetting.getType().equals(MsgTypeEnum.TASK_TIMEOUT_NOTICE.getCode()) ||
                            flowRuTimeoutSetting.getType().equals(MsgTypeEnum.TASK_WARNING_NOTICE.getCode())) {
                        //追加redis
                        if (Objects.nonNull(cacheManager)) {
                            cacheManager.set(flowRuTimeoutSetting.getId() + "::" + procInstId + "::" + flowRuTimeoutSetting.getType(),
                                    JSON.toJSONString(flowRuTimeoutSetting), flowRuTimeoutSetting.getDuration(), TimeUnit.SECONDS);
                        }
                    }
                }
            }
        } finally {
            // 存储在threaLocal中变量需要手动清除,防止内存溢出
            CommonGlobalParamSetHandle.remove();
        }
    }

    private String resolveStarter(String procInstId) {
        FlowProcessInstanceTraceVo ps = processInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(procInstId);
        if (Objects.nonNull(ps)) {
            return ps.getStarterId();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveActivityNoticeChannel(String deployId, String activityId, JsonNode candidateUsers, JsonNode noticeChannel) {
        //        Date currentDate = new Date();
        //        User currentUser = userRepository.getCurrentUser();
        //        FlowReTimeoutSetting timeoutSettingInfo = new FlowReTimeoutSetting();
        //        timeoutSettingInfo.setDeployId(deployId);
        //        timeoutSettingInfo.setActivityId(activityId);
        //        timeoutSettingInfo.setType(NoticeTypeEnum.TASK_START_NOTICE.getCode());
        //        timeoutSettingInfo.setCreateTime(currentDate);
        //        timeoutSettingInfo.setUpdateTime(currentDate);
        //        timeoutSettingInfo.setCreateBy(currentUser.getUserName());
        //        FlowNoticeTemplateVo flowNoticeTemplateVo = flowNoticeTemplateRepository.getTemplateListByType(NoticeTypeEnum.TASK_START_NOTICE.getCode()).get(0);
        //        if (Objects.nonNull(flowNoticeTemplateVo)){
        //            timeoutSettingInfo.setTitle(flowNoticeTemplateVo.getTitle());
        //            timeoutSettingInfo.setContent(flowNoticeTemplateVo.getContent());
        //        }
        //        flowReTimeoutSettingDAO.insertSelective(timeoutSettingInfo);
        //        saveTaskNoticeChannel(timeoutSettingInfo, candidateUsers, noticeChannel);
        return 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveTimeoutNoticeSetting(String deployId, String activityId, JsonNode timeoutSetting, Map<String, JsonNode> taskUsers) {
        User currentUser = userRepository.getCurrentUser();
        Date currentDate = new Date();
        boolean isTimeout = Boolean.parseBoolean(timeoutSetting.get("isTimeout").textValue());
        if (isTimeout) {
            //1.消息通知改版，对接消息中心不会使用到title和content，故这里先hard code
            String title = "";
            if (StringUtils.isBlank(activityId)) {
                //templateListByType = flowNoticeTemplateRepository.getTemplateListByType(MsgTypeEnum.PROCESS_TIMEOUT_NOTICE.getCode());
                title = MsgTypeEnum.PROCESS_TIMEOUT_NOTICE.getDescription();
            } else {
                //templateListByType = flowNoticeTemplateRepository.getTemplateListByType(MsgTypeEnum.TASK_TIMEOUT_NOTICE.getCode());
                title = MsgTypeEnum.TASK_TIMEOUT_NOTICE.getDescription();
            }
            String content = title;
            //2.构建超时配置实体
            FlowReTimeoutSetting timeoutSettingInfo = new FlowReTimeoutSetting();
            timeoutSettingInfo.setDeployId(deployId);
            timeoutSettingInfo.setActivityId(activityId);
            timeoutSettingInfo.setDuration(getDuration(timeoutSetting));
            timeoutSettingInfo.setCreateTime(currentDate);
            timeoutSettingInfo.setUpdateTime(currentDate);
            timeoutSettingInfo.setTitle(title);
            timeoutSettingInfo.setContent(content);
            timeoutSettingInfo.setCreateBy(currentUser.getUserName());
            //从配置的timeoutNotifier -> notifier中获取通知的人员信息
            JsonNode notifiers = resolveSettingNotifier(timeoutSetting.get("timeoutNotifier"));

            if (StringUtils.isBlank(activityId)) {
                //模型级别的超时配置，notifiers = null，默认只给部署流程的人发通知
                timeoutSettingInfo.setType(NoticeTypeEnum.PROCESS_TIMEOUT_NOTICE.getCode());
            } else {
                //节点级别的超时配置，获取节点的candidateUsers配置
                //notifiers = taskUsers.get(activityId);
                timeoutSettingInfo.setType(NoticeTypeEnum.TASK_TIMEOUT_NOTICE.getCode());
            }
            flowReTimeoutSettingDAO.insertSelective(timeoutSettingInfo);
            //3.保存超时与推送人员、渠道之间的关系
            saveTaskNoticeChannel(timeoutSettingInfo, notifiers);

            JsonNode warningSetting = timeoutSetting.get("warningSetting");
            if (warningSetting != null) {
                //解析保存预警相关的配置
                saveWarningSetting(deployId, activityId, currentUser, currentDate, timeoutSettingInfo, warningSetting);
            }
        }
        return 0;
    }

    @Override
    public List<FlowReTimeoutSettingVo> listFlowReTimeoutSetting(String deploymentId, String nodeCode, List<String> types) {
        // 封装查询条件
        FlowReTimeoutSettingExample queryCondition = new FlowReTimeoutSettingExample();

        // 添加条件
        FlowReTimeoutSettingExample.Criteria criteria = queryCondition.createCriteria();

        // 部署ID不为空时,则添加处理类型作为查询条件查询
        if(StringUtils.isNotBlank(deploymentId)) {
            criteria.andDeployIdEqualTo(deploymentId);
        }

        // 节点编码不为空时,则添加处理状态作为查询条件查询
        if(StringUtils.isNotBlank(nodeCode)) {
            criteria.andActivityIdEqualTo(nodeCode);
        }

        // 类型不为空时,则添加处理状态作为查询条件查询
        if(CollectionUtils.isNotEmpty(types)) {
            criteria.andTypeIn(types);
        }
        return listFlowReTimeoutSetting(queryCondition);
    }

    private JsonNode resolveSettingNotifier(JsonNode timeoutNotifier) {
        if (!timeoutNotifier.isNull()) {
            return timeoutNotifier.get("notifier");
        }
        return null;
    }

    private void saveWarningSetting(String deployId, String activityId, User currentUser, Date currentDate, FlowReTimeoutSetting timeoutSettingInfo, JsonNode warningSetting) {
        boolean isWarning = Boolean.parseBoolean(warningSetting.get("isWarning").textValue());
        if (isWarning) {
            JsonNode warningRegular = warningSetting.get("warningRegular");
            for (int i = 0; i < warningRegular.size(); i++) {
                JsonNode warningRegularItem = warningRegular.get(i);
                //从每项预警的配置里获取对应的通知人员信息
                JsonNode notifiers = resolveSettingNotifier(warningRegularItem.get("warningNotifier"));
                FlowReTimeoutSetting warningSettingInfo = new FlowReTimeoutSetting();
                String warnTitle = warningRegularItem.get("title") == null ? "" : warningRegularItem.get("title").textValue();
                String warnContent = warningRegularItem.get("content") == null ? "" : warningRegularItem.get("content").textValue();
                if (StringUtils.isBlank(warnTitle) || StringUtils.isBlank(warnContent)) {
                    List<FlowNoticeTemplateVo> templateListByType = new ArrayList<>();
                    if (StringUtils.isBlank(activityId)) {
                        templateListByType = flowNoticeTemplateRepository.getTemplateListByType(MsgTypeEnum.PROCESS_WARNING_NOTICE.getCode());
                    } else {
                        templateListByType = flowNoticeTemplateRepository.getTemplateListByType(MsgTypeEnum.TASK_WARNING_NOTICE.getCode());
                    }
                    if (CollectionUtils.isNotEmpty(templateListByType)) {
                        warnTitle = templateListByType.get(0).getTitle();
                        warnContent = templateListByType.get(0).getContent();
                    }
                }
                warningSettingInfo.setDeployId(deployId);
                warningSettingInfo.setActivityId(activityId);
                warningSettingInfo.setParentId(timeoutSettingInfo.getId());
                warningSettingInfo.setDuration(getDuration(warningRegularItem));
                warningSettingInfo.setWarningCode(warningRegularItem.get("warningCode").textValue());
                if (StringUtils.isBlank(activityId)) {
                    warningSettingInfo.setType(NoticeTypeEnum.PROCESS_WARNING_NOTICE.getCode());
                } else {
                    warningSettingInfo.setType(NoticeTypeEnum.TASK_WARNING_NOTICE.getCode());
                }
                warningSettingInfo.setCreateTime(currentDate);
                warningSettingInfo.setUpdateTime(currentDate);
                warningSettingInfo.setTitle(warnTitle);
                warningSettingInfo.setContent(warnContent);
                warningSettingInfo.setCreateBy(currentUser.getUserName());
                flowReTimeoutSettingDAO.insertSelective(warningSettingInfo);
                saveTaskNoticeChannel(warningSettingInfo, notifiers);
            }
        }
    }


    private void saveTaskNoticeChannel(FlowReTimeoutSetting timeoutSettingInfo, JsonNode notifiers) {
        Date currentDate = new Date();
        User currentUser = userRepository.getCurrentUser();
        List<FlowReTaskNoticeChannel> flowReTaskNoticeChannels = new ArrayList<>();
        if (Objects.nonNull(notifiers)) {
            for (int i = 0; i < notifiers.size(); i++) {
                FlowReTaskNoticeChannel taskNoticeChannel = new FlowReTaskNoticeChannel();
                taskNoticeChannel.setDeployId(timeoutSettingInfo.getDeployId());
                taskNoticeChannel.setActivityId(timeoutSettingInfo.getActivityId());
                taskNoticeChannel.setReTimeoutSettingId(timeoutSettingInfo.getId());
                JsonNode notifier = notifiers.get(i);
                //如果type是INIT,则说明是发起人
                if (Objects.equals(ConstanceVariable.STARTER_TYPE, notifier.get("type").textValue())) {
                    //获取该流程的发起人
                    taskNoticeChannel.setStaCode(ConstanceVariable.STARTER);
                    taskNoticeChannel.setStaType(ConstanceVariable.STARTER_TYPE);
                } else {
                    taskNoticeChannel.setStaCode(notifier.get("code").textValue());
                    taskNoticeChannel.setStaType(notifier.get("type").textValue());
                }
                taskNoticeChannel.setStaJson(notifier.toString());
                taskNoticeChannel.setCreateBy(currentUser.getUserName());
                taskNoticeChannel.setChannel("SYSTEM");
                taskNoticeChannel.setCreateTime(currentDate);
                taskNoticeChannel.setUpdateTime(currentDate);
                flowReTaskNoticeChannels.add(taskNoticeChannel);
            }
        } else {
            FlowReTaskNoticeChannel taskNoticeChannel = new FlowReTaskNoticeChannel();
            taskNoticeChannel.setDeployId(timeoutSettingInfo.getDeployId());
            taskNoticeChannel.setActivityId(timeoutSettingInfo.getActivityId());
            taskNoticeChannel.setReTimeoutSettingId(timeoutSettingInfo.getId());
            taskNoticeChannel.setStaCode(currentUser.getUserCode());
            taskNoticeChannel.setCreateBy(currentUser.getUserName());
            taskNoticeChannel.setChannel("SYSTEM");
            taskNoticeChannel.setCreateTime(currentDate);
            taskNoticeChannel.setUpdateTime(currentDate);
            flowReTaskNoticeChannels.add(taskNoticeChannel);
        }
        if (!flowReTaskNoticeChannels.isEmpty()) {
            noticeTemplateDAO.insertTaskNoticeChannelBatch(flowReTaskNoticeChannels);
        }
    }

    private Long getDuration(JsonNode jsonNode) {
        Long day = jsonNode.get("day").asLong();
        Long hour = jsonNode.get("hour").asLong();
        Long minute = jsonNode.get("minute").asLong();
        Long duration = day * 24 * 3600 + hour * 3600 + minute * 60;
        return duration;
    }

    private FlowReTimeoutSetting convert(FlowReTimeoutSettingVo entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        FlowReTimeoutSetting result = new FlowReTimeoutSetting();
        BeanUtils.copyProperties(entity, result);
        return result;
    }

    private FlowReTimeoutSettingVo convert(FlowReTimeoutSetting entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        FlowReTimeoutSettingVo result = new FlowReTimeoutSettingVo();
        BeanUtils.copyProperties(entity, result);
        return result;
    }
}
